#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
// #include <pthread.h>
#include "tbf.h"

typedef void (*sighandler_t)(int);

// static pthread_mutex_t mut_job = PTHREAD_MUTEX_INITIALIZER;
static struct tbf_st* job[TBF_MAX];
static void inited = 0;
static sighandler_t alrm_handler_save;

struct tbf_st
{
    int cps;
    int burst;
    int token;
    int pos;

    // pthread_mutex_t mut; 
    /* data */
};


static void alrm_hadler(int s){
    int i;

    alarm(1);

    for( i  = 0; i < TBF_MAX; i++){
        if (job[i] != NULL){
            job[i] -> token += job[i]->cps;
            if (job[i] -> token > job[i] -> burst)
                job[i] -> token = job[i] -> burst;
        }
    }
}

static void module_unload(void){
    int i;

    signal(SIGALRM, alrm_hadler_save);
    alarm(0);
    for(i = 0; i < TBF_MAX; i++)
        free(job[i]);
}

static void module_load(void){
    alrm_hadler_save = signal(SIGALRM, alrm_hadler);
    alarm(1);

    atexit(module_unload);
}

static int get_free_pos(void){
    int i;

    for(i = 0; i < TBF_MAX; i++){
        if(job[i] == NULL)
            return i;
    }
    return -1;
}

tbf_t *tbf_init(int cps, int burst){
    struct tbf_st *me;
    int pos;

    if (!inited){
        module_load();
        inited = 1;
    }
    me = malloc(sizeof(*me));
    if (me == NULL)
        return NULL;

    me -> token = 0;
    me -> cps = cps;
    me -> burst = burst;
    // pthread_mutex_lock(&mut_job);
    pos = get_free_pos();
    if (pos < 0){
        //   pthread_mutex_unlock(&mut_job);
        free(me);
        return NULL;
    }
    me -> pos = pos;
    job[pos] = me;
    // pthread_mutex_unlock(&mut_job);
    return me;
}

static int min(int a , int b){
    if( a < b)
        return a;
    return b;
}

int tbf_fetchtoken(tbf_t *ptr, int size){
    struct tbf_st *me = ptr;
    int n;

    if(size <= 0)
        return -EINVAL;

    // pthread_mutex_lock(&me->mut);
    while(me->token <= 0 ){
        // pthread_mutex_unlock(&me->mut);
        sched_yield();
        // pthread_mutex_lock(&me->mut);
    }
        

    n = min(me->token, size);
  // pthread_mutex_unlock(&me->mut);
    me->token -= n;
    return n;
}


int tbf_returntoken(tbf_t *ptr, int size){
    struct tbf_st *me = ptr;
    
    if( size <= 0)  
        return -EINVAL;
     // pthread_mutex_lock(&me->mut);
    me -> token += size;
    if( me -> token > me->burst)    
        me->token = me->burst;
      // pthread_mutex_unlock(&me->mut);
    return size;
}

int tbf_destroy(tbf_t *ptr){
    struct  tbf_st *me = ptr;

    // pthread_mutex_lock(&mut_job);
    job[me->pos] = NULL;
    // pthread_mutex_unlock(&mut_job); 
    free(ptr);

    return 0;
    
}